package njust.csc.aecore.judge

import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import njust.csc.aecore.entity.*
import njust.csc.aecore.utils.ImageUtils
import njust.csc.aecore.utils.NetworkUtils
import njust.csc.aecore.utils.SerializeUtils
import java.nio.file.Path
import kotlin.io.path.extension
import kotlin.io.path.listDirectoryEntries

private val logger = KotlinLogging.logger {}

/**
 * @Author ZhouHang
 * @Date 24/6/27 周四
 */
val promptForText = """
你是一名公安机关工作人员，是安卓app反诈专家，能够从给定的信息中判断一个apk是否涉嫌诈骗。
我将会为你提供一个app的部分信息，包括app名称{app_name}、app包名{app_package}、app页面文本{screen_content}，app签名{signature}，app权限{permission}，请您为我判断这个app是否存在涉嫌赌博、色情、诈骗的可能。
注意：你的第一行输出应该是一个具体的类别:“赌博”、“色情”、“诈骗”、“普通”，第二行请用不超过三句话给出理由，请不要用第一人称。
注意：请你通过以下几个方面来进行分析：

对于{app_name}和{screen_content}，主要通过关键词进行判断：
赌博类app，在app_name和screen_content中，往往会出现诸如“娱乐，真钱，现金，赌博，棋牌，皇冠，博彩，”一类具有明显赌博暗示或者常见赌博形式的关键词。
色情类app，在app_name和screen_content中，往往会出现诸如“成人，无码，色，性爱，淫，色情”一类具有明显色情暗示的关键词。
诈骗类app，在app_name和screen_content中，往往会出现诸如“贷款，保底，赚，质押，抵押，放款，借贷，聊天，证券，切换线路等”一类具有欺诈和诱骗钱财的关键词。

对于{app_package}，主要通过字符进行判断：
你需要关注其中是否包含乱码或随机字符，若含有乱码或随机字符，则具有涉嫌赌博、色情、诈骗的可能。

{signature} 是一个逗号分隔的字符串，其中CN表示域名或组织名，OU为组织名，O为组织名，L为城市名，ST为地区名，C为国家名
这些名称若缺乏信息量，存在乱码，随机数字，或长度超过15个字符，或C国家名称不在中国，都可能涉嫌诈骗。签名信息需要重点考察和研判。

对于{permission}，涉诈应用可能会要求过于多的危险的权限，尤其是敏感权限，但权限并不是绝对因素。

{app_name}: %s
{app_package}: %s
{screen_content}: %s
{signature}: %s
{permission}: %s

{output}:
""".trimIndent()

val promptForImage = """
我将会为你提供一个app的部分屏幕截图，请您为我判断这个app是否存在涉嫌赌博、色情、诈骗的可能。
注意：你的第一行输出应该是一个具体的类别:“赌博”、“色情”、“诈骗”、“普通”，第二行请用不超过三句话给出理由，请不要用第一人称。
注意：请你通过以下几个方面来进行分析：

内容审查：
1、查看屏幕截图中是否有明显的赌博活动元素，比如扑克牌、骰子、轮盘等赌博工具的图像，或者有关于赌博的文字描述。
2、审查是否含有不恰当的色情内容，这些可能通过裸露、低俗的行为表现或文字描述来展现。
3、检查是否有诱导性的诈骗内容，例如“下款快”，“利息低”，“收益高”，“切换线路”，承诺高额回报的投资、中奖信息、非常低廉的商品销售等可能诱使用户进行非理性操作的信息。

界面风格和布局：
1、非正规或涉嫌违法的app通常在设计上不够专业，会有较多的设计缺陷。
2、检查是否有频繁的弹窗广告或者诱导点击的按钮布局，这些都可能是诈骗行为的一部分。

交互元素和功能：
1、分析截图中显示的app功能，特别是那些涉及金钱交易的功能，比如充值、提现、转账等，看它们是否合理，是否有可能用于诈骗。
2、查看是否有用户反馈、举报机制的存在，合规的应用通常会有完善的用户反馈渠道。

文本内容：
1、注意审查屏幕截图中的文字内容，是否有使用过度夸大、误导性、色情语言。
2、分析是否存在不实的承诺，比如保证能赚大钱、快速变富等。
""".trimIndent()


class GPTJudge : LLMJudge {
    override fun ask(apkInfo: String): String {
        val apk = SerializeUtils.load(apkInfo)
        return ask(apk)
    }

    override fun ask(apk: APK): String {
        // 只将 subject 信息传给模型
        var subject = apk.v1SignatureInfo.certification.subject
        if (subject == "") subject = apk.v2SignatureInfo.certification.subject
        if (subject == "") subject = apk.v3SignatureInfo.certification.subject

        val body = ZZZRequestBody(
            model = "gpt-4-turbo-preview",
            messages = listOf(
                Message(
                    role = Role.USER,
                    content = promptForText.format(
                        apk.name,
                        apk.packageName,
                        apk.screenContent,
                        subject,
                        apk.permissions
                    )
                )
            )
        )
        val bodyString = Json.encodeToString(body)
        logger.info { bodyString }
        val response = NetworkUtils.post(bodyString)
        return response
    }

    /**
     * 用图片询问大模型
     * */
    fun askVision(imagePath: Path): String {
        var base64 = ""
        for (png in imagePath.listDirectoryEntries()) {
            if (png.extension == "png") {
                base64 = ImageUtils.base64Encode(png)
                break
            }
        }
        if (base64 == "") {
            return "No"
        }

        val body = hashMapOf(
            "model" to "gpt-4-vision-preview",
            "messages" to listOf(
                hashMapOf(
                    "role" to "user",
                    "content" to listOf(
                        hashMapOf(
                            "type" to "text",
                            "text" to promptForImage
                        ),
                        hashMapOf(
                            "type" to "image_url",
                            "image_url" to hashMapOf("url" to "data:image/jpeg;base64,$base64")
                        )
                    )
                )
            )
        )
        val ret = NetworkUtils.post(SerializeUtils.toJson(body))
        if (ret == "") {
            return "普通"
        }
        return ret
    }
}
